#include "StrInStream.h"

#include <string.h>
#include <fan-llvm/core/Core.h>
#include "FanInt.h"
#include "List.h"
#include "Str.h"
#include "Type.h"
#include "UnsupportedErr.h"


Type* StrInStream_classType = NULL;
VTable* StrInStream_classVTable = NULL;


/**********************************************
 *
 * Methods and fields
 *
 **********************************************/


/* Int? read() */
NullableInt StrInStream_read(InStream* self)
{
  exceptionThrow(UnsupportedErr_newWithFormattedMsg("Binary read on Str.in"));
  return VAL_NULL;
}


/* Int? readBuf(Buf buf, Int n) */
NullableInt StrInStream_readBuf(InStream* self, Buf* buf, Int n)
{
  exceptionThrow(UnsupportedErr_newWithFormattedMsg("Binary read on Str.in"));
  return VAL_NULL;
}


/* This unread(Int b) */
InStream* StrInStream_unread(InStream* self, Int b)
{
  exceptionThrow(UnsupportedErr_newWithFormattedMsg("Binary read on Str.in"));
  return NULL;
}


/* Int? readChar() */
NullableInt StrInStream_readChar(InStream* self)
{
  StrInStream* in = (StrInStream*)self;
  if (in->pushback != NULL && !List_isEmpty(in->pushback))
    return List_pop(in->pushback);
  if (in->pos >= in->str->len)
    return VAL_NULL;
  return VAL_MAKE_INT(in->str->buffer[in->pos++]);
}


/* This unreadChar(Int b) */
InStream* StrInStream_unreadChar(InStream* self, Int b)
{
  StrInStream* in = (StrInStream*)self;
  if (in->pushback == NULL)
    in->pushback = List_new(Int_classType, 8);
  List_push(in->pushback, VAL_MAKE_INT(b));
  return self;
}


/**********************************************
 *
 * Extra methods
 *
 **********************************************/


/* Create a new StrInStream instance that reads chars from the given Str */
StrInStream* StrInStream_new(Str* str)
{
  StrInStream* inStream = (StrInStream*)Obj_allocate(sizeof(StrInStream), StrInStream_classVTable);
  InStream_make((InStream*)inStream, NULL);
  inStream->str = str;
  inStream->pos = 0;
  inStream->pushback = NULL;
  return inStream;
}


/**********************************************
 *
 * Type-system functions
 *
 **********************************************/


Type* StrInStream_initType(void)
{
  if (StrInStream_classType == NULL)
  {
    Type* baseType = InStream_initType();
    VTable* vtable = StrInStream_initVTable();
    Type* type = Type_new("StrInStream", baseType, vtable, sizeof(StrInStream), sizeof(StrInStreamVTable));
    Type_registerMethodImplementation(type, "read", InStream_read, 0);
    Type_registerMethodImplementation(type, "readBuf", InStream_readBuf, 2);
    Type_registerMethodImplementation(type, "readChar", InStream_readChar, 0);
    Type_registerMethodImplementation(type, "unread", InStream_unread, 1);
    Type_registerMethodImplementation(type, "unreadChar", InStream_unreadChar, 1);
    StrInStream_classType = type;
    StrInStream_initStatic();
  }
  return StrInStream_classType;
}


VTable* StrInStream_initVTable(void)
{
  if (StrInStream_classVTable == NULL)
  {
    VTable* vtable = memoryAlloc(sizeof(StrInStreamVTable));
    memcpy(vtable, InStream_initVTable(), sizeof(InStreamVTable));
    ((InStreamVTable*)vtable)->read = InStream_read;
    ((InStreamVTable*)vtable)->readBuf = InStream_readBuf;
    ((InStreamVTable*)vtable)->readChar = InStream_readChar;
    ((InStreamVTable*)vtable)->unread = InStream_unread;
    ((InStreamVTable*)vtable)->unreadChar = InStream_unreadChar;
    StrInStream_classVTable = vtable;
  }
  return StrInStream_classVTable;
}


void StrInStream_initStatic(void)
{
}
